Before formally defining .NET delegates, let’s gain a bit of perspective. Historically, the Windows API made frequent use of C-style function pointers to create entities termed callback functions, or simply callbacks. Using callbacks, programmers were able to configure one function to report back to (call back) another function in the application. With this approach, Windows developers were able to handle button-clicking, mouse-moving, menu-selecting, and general bidirectional communications between two entities in memory.
The problem with standard C-style callback functions is that they represent little more than a raw address in memory. Ideally, you should be able to configure callbacks to include additional type-safe information such as the number of (and types of) parameters and the return type (if any) of the method pointed to. Sadly, this is not the case in traditional callback functions and, as you may suspect, they can therefore be a frequent source of bugs, hard crashes, and other runtime disasters. Nevertheless, callbacks are useful entities.
In the .NET Framework, callbacks are still possible, and this functionality is accomplished in a much safer and more object-oriented manner using delegates. In essence, a delegate is a type-safe object that points to another method (or possibly a list of methods) in the application, which can be invoked at a later time. Specifically, a delegate maintains three important pieces of information:
Note .NET delegates can point to either static or instance methods.
Once a delegate object has been created and given the necessary information, it may dynamically invoke the method(s) it points to at runtime. Every delegate in the .NET Framework (including your custom delegates) is automatically endowed with the ability to call its methods synchronously or asynchronously. This fact greatly simplifies programming tasks, given that you can call a method on a secondary thread of execution without manually creating and managing a Thread object.
Note We will examine the asynchronous behavior of delegate types during our investigation of the System.Threading namespace in Chapter 19.